package com.hu.bgp.client.eventservice;

import java.util.List;
import java.util.Set;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.ServiceDefTarget;

import de.novanic.eventservice.client.command.RemoteCommand;
import de.novanic.eventservice.client.command.RemoteListenCommand;
import de.novanic.eventservice.client.event.DefaultRemoteEventConnector;
import de.novanic.eventservice.client.event.DomainEvent;
import de.novanic.eventservice.client.event.domain.Domain;
import de.novanic.eventservice.client.event.filter.EventFilter;
import de.novanic.eventservice.client.event.listener.unlisten.UnlistenEvent;
import de.novanic.eventservice.client.event.listener.unlisten.UnlistenEventListener;
import de.novanic.eventservice.client.event.service.EventService;
import de.novanic.eventservice.client.event.service.EventServiceAsync;

/**
 * Stub nodig om createEventService() te overriden en zo te compenseren voor de
 * tekortkomingen van gwteventservice.
 * 
 * @author ejvos
 */
public class BgpRemoteEventConnector extends DefaultRemoteEventConnector
{
	private EventServiceAsync myEventService;

	/**
	 * Creates a new RemoteEventConnector with a connection to
	 * {@link EventService}.
	 */
	BgpRemoteEventConnector()
	{
		this(createEventService());
	}

	/**
	 * Creates a new RemoteEventConnector with a connection to the corresponding
	 * EventService.
	 * 
	 * @param anEventServiceAsync
	 *            EventService for the connection
	 */
	BgpRemoteEventConnector(EventServiceAsync anEventServiceAsync)
	{
		myEventService = anEventServiceAsync;
	}

	/**
	 * That method is called to execute the first server call (for
	 * initialization).
	 * 
	 * @param aCallback
	 *            callback
	 */
	public void init(AsyncCallback<Void> aCallback)
	{
		myEventService.initEventService(aCallback);
	}

	/**
	 * Activates the connector for the domain. An
	 * {@link de.novanic.eventservice.client.event.filter.EventFilter} to filter
	 * events on the server side is optional.
	 * 
	 * @param aDomain
	 *            domain to activate
	 * @param anEventFilter
	 *            EventFilter to filter the events on the server side (optional)
	 * @param aCallback
	 *            callback
	 */
	public void activateStart(Domain aDomain, EventFilter anEventFilter, AsyncCallback<Void> aCallback)
	{
		myEventService.register(aDomain, anEventFilter, aCallback);
	}

	/**
	 * Deactivates the connector for the domains (no events can be got from the
	 * domains).
	 * 
	 * @param aDomains
	 *            domains to deactivate
	 * @param aCallback
	 *            callback
	 */
	public void deactivate(Set<Domain> aDomains, AsyncCallback<Void> aCallback)
	{
		myEventService.unlisten(aDomains, aCallback);
	}

	/**
	 * Deactivates the connector for the domain (no events can be got from the
	 * domain).
	 * 
	 * @param aDomain
	 *            domain to deactivate
	 * @param aCallback
	 *            callback
	 */
	public void deactivate(Domain aDomain, AsyncCallback<Void> aCallback)
	{
		myEventService.unlisten(aDomain, aCallback);
	}

	/**
	 * Registers an
	 * {@link de.novanic.eventservice.client.event.listener.unlisten.UnlistenEvent}
	 * to the server side which will be triggered when a timeout or
	 * unlisten/deactivation for a domain occurs.
	 * 
	 * @param anUnlistenScope
	 *            scope of the unlisten events to receive
	 * @param anUnlistenEvent
	 *            {@link de.novanic.eventservice.client.event.listener.unlisten.UnlistenEvent}
	 *            which can contain custom data
	 * @param aCallback
	 *            callback
	 */
	public void registerUnlistenEvent(UnlistenEventListener.Scope anUnlistenScope, UnlistenEvent anUnlistenEvent, AsyncCallback<Void> aCallback)
	{
		super.registerUnlistenEvent(anUnlistenScope, anUnlistenEvent, aCallback);
		// The UnlistenEvent mustn't be registered to the server side, when the
		// scope is local.
		if (UnlistenEventListener.Scope.LOCAL != anUnlistenScope)
		{
			myEventService.registerUnlistenEvent(anUnlistenScope, anUnlistenEvent, aCallback);
		}
	}

	/**
	 * Registers an
	 * {@link de.novanic.eventservice.client.event.filter.EventFilter} for a
	 * domain. That can be used when the domain is already activated and an
	 * {@link de.novanic.eventservice.client.event.filter.EventFilter} is needed
	 * later or isn't available when the domain becomes active.
	 * 
	 * @param aDomain
	 *            domain
	 * @param anEventFilter
	 *            EventFilter to filter the events on the server side (optional)
	 * @param aCallback
	 *            callback
	 */
	public void registerEventFilter(Domain aDomain, EventFilter anEventFilter, AsyncCallback<Void> aCallback)
	{
		myEventService.registerEventFilter(aDomain, anEventFilter, aCallback);
	}

	/**
	 * Deregisters the
	 * {@link de.novanic.eventservice.client.event.filter.EventFilter} for a
	 * domain.
	 * 
	 * @param aDomain
	 *            domain to remove the EventFilter from
	 * @param aCallback
	 *            callback
	 */
	public void deregisterEventFilter(Domain aDomain, AsyncCallback<Void> aCallback)
	{
		myEventService.deregisterEventFilter(aDomain, aCallback);
	}

	/**
	 * Starts listening for events (listen call to the server side).
	 * 
	 * @param aCallback
	 *            callback
	 */
	protected void listen(AsyncCallback<List<DomainEvent>> aCallback)
	{
		RemoteCommand<List<DomainEvent>> theRemoteListenCommand = new RemoteListenCommand();
		theRemoteListenCommand.init(aCallback);
		theRemoteListenCommand.execute(myEventService);
	}

	/**
	 * Creates an instance of the EventService.
	 * 
	 * @return EventService
	 */
	private static EventServiceAsync createEventService()
	{
		final String theServiceURL = "/bgp/gwteventservice";
		ServiceDefTarget theServiceEndPoint = (ServiceDefTarget) GWT.create(EventService.class);
		theServiceEndPoint.setServiceEntryPoint(theServiceURL);
		return (EventServiceAsync) theServiceEndPoint;
	}
}
